﻿using System;
using System.Windows.Input;
using Microsoft.Practices.Prism.Commands;
using Mitosoft.Definitions.Data;
using Mitosoft.Definitions.ViewModels;
using TextFileReaderModule.Interfaces;

namespace TextFileReaderModule.ViewModels
{
  /// <summary>
  /// View model representing IGenericDataBlockItem data, 
  /// extended by other properties.
  /// </summary>
  public class GenericDataBlockItemViewModel : 
    BaseViewModel,
    IGenericDataBlockItem<string>, 
    ICommentable
  {
    #region delegates, events and actions

    /// <summary>
    /// Action initiated whenever one of some specific properties changes.
    /// </summary>
    public Action<GenericDataBlockItemViewModel> _specificPropertyChangedAction;

    #endregion

    #region fields

    /// <summary>
    /// Represents <see cref="Id"/>
    /// </summary>
    protected ulong _id;

    /// <summary>
    /// Represents <see cref="Value"/>
    /// </summary>
    protected string _value;

    /// <summary>
    /// Represents <see cref="Comment"/>
    /// </summary>
    protected string _comment;

    /// <summary>
    /// Represents <see cref="ShowHideCommentCommand"/>
    /// </summary>
    protected ICommand _showHideCommentCommand;

    /// <summary>
    /// Represents <see cref="CommentIsVisible"/>
    /// </summary>
    protected bool _commentIsVisible;

    /// <summary>
    /// Represents the service handling comments.
    /// </summary>
    protected ICommentService _commentService;

    /// <summary>
    /// Represents <see cref="SourceIndex"/>
    /// </summary>
    protected int _sourceIndex;

    #endregion

    #region constructors and destructor

    /// <summary>
    /// Creates a new <see cref="GenericDataBlockItemViewModel"/> instance.
    /// </summary>
    /// <param name="commentService">Represents the service handling comments.</param>
    /// <param name="id">Represents <see cref="Id"/></param>
    /// <param name="sourceIndex">Represents the source index.</param>
    /// <param name="value">Represents <see cref="Value"/></param>
    /// <param name="specificPropertyChangedAction">Action initiated whenever one of some specific properties changes.</param>
    public GenericDataBlockItemViewModel(
      ICommentService commentService,
      ulong id,
      int sourceIndex,
      string value,
      Action<GenericDataBlockItemViewModel> specificPropertyChangedAction)
    {
      // save values:
      _commentService = commentService;
      Id = id;
      Value = value;
      SourceIndex = sourceIndex;

      // Order may be important to set first the action and then to assign properties, 
      // especially the "specific" ones:
      _specificPropertyChangedAction = specificPropertyChangedAction;

      // get comment from service:
      Comment = _commentService.GetComment(Id);
    }

    /// <summary>
    /// Destructor. Will run only if Dispose has not been called.
    /// </summary>
    ~GenericDataBlockItemViewModel()
    {
      // Only for debugging: We want to know if the models are disposed:
      //Debug.Print("{0} will be disposed", Id);
      Dispose(false);
    }

    #endregion

    #region properties

    /// <summary>
    /// Gets or sets the source index.
    /// </summary>
    public int SourceIndex
    {
      get { return _sourceIndex; }
      set
      {
        if (value == _sourceIndex) return;
        _sourceIndex = value;
        HandlePropertyChanged();
      }
    }

    /// <summary>
    /// Gets or sets whether the comment is visible.
    /// </summary>
    public bool CommentIsVisible
    {
      get { return _commentIsVisible; }

      set
      {
        if (value == _commentIsVisible)
          return;   // Nothing changed.

        _commentIsVisible = value;

        HandlePropertyChanged();
        // ReSharper disable once ExplicitCallerInfoArgument
        HandlePropertyChanged("ShowHideCommentCommandText");
        // ReSharper disable once ExplicitCallerInfoArgument
        HandlePropertyChanged("ShowHideCommentCommandToolTip");

        var actionHandler = _specificPropertyChangedAction;
        if (actionHandler != null)
          actionHandler(this);
      }
    }

    /// <summary>
    /// Gets the text used for <see cref="ShowHideCommentCommand"/>.
    /// </summary>
    public string ShowHideCommentCommandText
    {
      get { return CommentIsVisible ? "-" : "+"; }
    }

    /// <summary>
    /// Gets the tool tip used for <see cref="ShowHideCommentCommand"/>.
    /// </summary>
    public string ShowHideCommentCommandToolTip
    {
      get { return CommentIsVisible ? "Click to hide the comment line." : "Click to show the comment line."; }
    }

    /// <summary>
    /// Represents the command used to let show or hide the comment in the view.
    /// Used by the view.
    /// </summary>
    public ICommand ShowHideCommentCommand
    {
      get 
      {
        return 
          _showHideCommentCommand ?? 
            (_showHideCommentCommand = 
              new DelegateCommand(
                ShowHideCommentCommandExecuted));
      }
    }

    /// <summary>
    /// Gets or sets a comment
    /// </summary>
    public string Comment
    {
      get { return _comment; }
      set
      {
        if (value == _comment)
          return; // Nothing changed.

        _comment = value;

        _commentService.Add(Id, value, SourceIndex);

        HandlePropertyChanged();

        var actionHandler = _specificPropertyChangedAction;
        if (actionHandler != null)
          actionHandler(this);
      }
    }

    /// <summary>
    /// Gets or sets the Id.
    /// </summary>
    public ulong Id
    {
      get { return _id; }

      set
      {
        _id = value;

        HandlePropertyChanged();
        // ReSharper disable once ExplicitCallerInfoArgument
        HandlePropertyChanged("CorrectedId");
      }
    }

    /// <summary>
    /// Gets the corrected Id.
    /// </summary>
    public ulong CorrectedId
    {
      get { return Id + 1; }
    }

    /// <summary>
    /// Gets or sets the value.
    /// </summary>
    public string Value
    {
      get { return string.Format("<Bold><![CDATA[{0}]]></Bold>", _value); }

      set
      {
        _value = value;

        HandlePropertyChanged();
      }
    }

    #endregion

    #region methods

    /// <summary>
    /// The command <see cref="ShowHideCommentCommand"/> was executed.
    /// </summary>
    protected void ShowHideCommentCommandExecuted()
    {
      CommentIsVisible = !CommentIsVisible;
    }

    /// <summary>
    /// Creates a new <see cref="GenericDataBlockItemViewModel"/> based on the given source.
    /// </summary>
    /// <param name="source">Contains the basic data which will be used to create a new instance.</param>
    /// <param name="sourceIndex">Represents the source id.</param>
    /// <param name="commentService">Service used to handle the comments.</param>
    /// <param name="specificPropertyChangedAction">Action initiated whenever one of some specific properties changes.</param>
    /// <returns>New created instance.</returns>
    /// <exception cref="ArgumentNullException">Will be thrown if the given source is null.</exception>
    public static IGenericDataBlockItem<string> Instance(
      IGenericDataBlockItem<string> source,
      int sourceIndex,
      ICommentService commentService,
      Action<GenericDataBlockItemViewModel> specificPropertyChangedAction
    )
    {
      if (source == null )
        throw new ArgumentNullException("source");

      return 
        new GenericDataBlockItemViewModel(
          commentService, source.Id, sourceIndex,
          source.Value, specificPropertyChangedAction);
    }

    #endregion
  }
}
